package com.xueyi.game.backend.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.xueyi.core.base.TableData;
import com.xueyi.core.constants.IsStatus;
import com.xueyi.core.constants.SystemCode;
import com.xueyi.core.expection.BusinessException;
import com.xueyi.core.utils.IpUtil;
import com.xueyi.core.utils.JWTUtil;
import com.xueyi.core.utils.LogUtil;
import com.xueyi.game.backend.config.BaseSqlConfig;
import com.xueyi.game.backend.config.GatewayConfig;
import com.xueyi.game.backend.entity.dto.BaseUserLoginDto;
import com.xueyi.game.backend.entity.dto.FindUserLogByUserIdDto;
import com.xueyi.game.backend.entity.dto.SaveBaseUserDto;
import com.xueyi.game.backend.entity.dto.UserTableDto;
import com.xueyi.game.backend.entity.po.BaseLog;
import com.xueyi.game.backend.entity.po.BaseMenu;
import com.xueyi.game.backend.entity.po.BaseUser;
import com.xueyi.game.backend.entity.vo.MenuListVo;
import com.xueyi.game.backend.entity.vo.UserLoginVo;
import com.xueyi.game.backend.entity.vo.UserTableVo;
import com.xueyi.game.backend.mapper.BackendBaseAccountMapper;
import com.xueyi.game.backend.service.IBackendAccountService;
import com.xueyi.game.backend.utils.BaseUserUtil;
import com.xueyi.starter.utils.FinalUtil;
import com.xueyi.starter.utils.JedisUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 描述 后台用户Service
 *
 * @auther 俞烨
 * @date 2022-11-08 15
 */
@Service
@Slf4j
@AllArgsConstructor
public class BackendAccountServiceImpl implements IBackendAccountService {

    private final BackendBaseAccountMapper backendBaseAccountMapper;
    private final BaseSqlConfig baseSqlConfig;
    private final JedisUtil jedisUtil;
    private final BaseUserUtil baseUserUtil;
    private final LogUtil logUtil;

    @Override
    public UserLoginVo<?> userLogin(BaseUserLoginDto baseUserLoginReq,
                                    HttpServletRequest request) throws BusinessException {

        logUtil.info("用户登录: {} , {} , IP: {}", baseUserLoginReq.getAccount(), JSON.toJSONString(baseUserLoginReq),
                IpUtil.getIpAddress(request));

        BaseUser baseUser = backendBaseAccountMapper.userLogin(baseUserLoginReq.getAccount(),
                baseUserLoginReq.getPassword(),
                baseSqlConfig.getUserTableName());

        if (baseUser == null || baseUser.getIsValid().equals(IsStatus.NO)) {
            throw new BusinessException(SystemCode.QUERY_CHECK_ERROR, "用户名密码不存在或已被禁用");
        }


        // 存入Redis中
        Jedis jedis = jedisUtil.getJedis();
        try {
            // 用户登录成功，查询用户的角色列表
            baseUser.setRoleList(baseUserUtil.findUserRoleList(baseUser.getId()));
            String tokenDetail = IdUtil.randomUUID();


            // 生成JWT令牌
            String signed = JWTUtil.createJwtToken(baseUser, tokenDetail, DateUtils.addDays(new Date(), 3));

            // 存入该用户账号对应的token列表
//            jedis.lpush("USER_LOGIN_TOKEN_LIST:" + baseUser.getId(),
//                    tokenDetail);
//            // 存入用户的基本信息
//            jedis.setex(baseSqlConfig.getUserTableName() + ":" +
//                            tokenDetail, 3600 * 24 * 7,
//                    JSON.toJSONString(baseUser));
            logUtil.info("用户登录成功: {} , {}, IP: {}", baseUserLoginReq.getAccount(),
                    JSON.toJSONString(baseUserLoginReq),
                    IpUtil.getIpAddress(request));
            return new UserLoginVo<BaseUser>(signed,
                    baseUser);
        } finally {
            FinalUtil.unLockAndCloseRedis(jedis);
        }
    }

    @Override
    public void clearUserCache(Integer userId) {

        Jedis jedis = jedisUtil.getJedis();
        try {
            // 清空用户的token，让用户重新登录
            List<String> userUseTokenList = jedis.lrange("USER_LOGIN_TOKEN_LIST:" + userId, 0, -1);
            if (!CollectionUtils.isEmpty(userUseTokenList)) {
                for (String userToken : userUseTokenList) {
                    jedis.del(baseSqlConfig.getUserTableName() + ":" + userToken);
                }
            }
            // 清空用户的菜单列表
            jedis.del("EDU_SYSTEM_ADMIN_LEFT_MENU:" + userId);
        } finally {
            FinalUtil.unLockAndCloseRedis(jedis);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(SaveBaseUserDto saveBaseUser) throws BusinessException {
        logUtil.info("save user : {} , {}", saveBaseUser.getAccount(),
                JSON.toJSONString(saveBaseUser));
        try {
            backendBaseAccountMapper.save(saveBaseUser, baseSqlConfig.getUserTableName());
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(SystemCode.QUERY_CHECK_ERROR, "用户名已存在，请更换");
        }

        // 处理roleIds
        String[] roleSplit = saveBaseUser.getRoleIds().split(",");
        backendBaseAccountMapper.batchSaveRole(roleSplit,
                saveBaseUser.getId(),
                baseSqlConfig.getUserJoinRoleTableName());

        logUtil.info("save user success : {} , {}", saveBaseUser.getAccount(),
                JSON.toJSONString(saveBaseUser));

        BaseUser user = GatewayConfig.USER_LOCAL.get();
        baseUserUtil.saveLog(user.getId() + "", "【后台用户管理】用户：" + user.getuName() + " 新增了用户 " + saveBaseUser.getuName());
    }


    @Override
    public BaseUser findUserById(Integer userId) throws BusinessException {

        logUtil.info("查询用户信息: {}", userId);

        SaveBaseUserDto userDto = backendBaseAccountMapper.findUserById(userId,
                baseSqlConfig.getUserTableName());
        if (userDto == null) {
            throw new BusinessException(SystemCode.QUERY_CHECK_ERROR, "该用户已被删除");
        }
        userDto.setRoleList(baseUserUtil.findUserRoleList(userDto.getId()));
        return userDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(SaveBaseUserDto saveBaseUser) throws BusinessException {
        logUtil.info("edit user : {} , {}", saveBaseUser.getAccount(),
                JSON.toJSONString(saveBaseUser));
        try {
            backendBaseAccountMapper.edit(saveBaseUser, baseSqlConfig.getUserTableName());
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(SystemCode.QUERY_CHECK_ERROR, "用户名已存在，请更换");
        }

        // 删除用户之前所有的角色列表
        backendBaseAccountMapper.delUserRole(saveBaseUser.getId(),
                baseSqlConfig.getUserJoinRoleTableName());
        // 处理roleIds
        String[] roleSplit = saveBaseUser.getRoleIds().split(",");
        backendBaseAccountMapper.batchSaveRole(roleSplit,
                saveBaseUser.getId(),
                baseSqlConfig.getUserJoinRoleTableName());

        // 清空用户在缓存中的数据信息
        clearUserCache(saveBaseUser.getId());

        logUtil.info("edit user success : {} , {}", saveBaseUser.getAccount(),
                JSON.toJSONString(saveBaseUser));

        BaseUser user = GatewayConfig.USER_LOCAL.get();
        baseUserUtil.saveLog(user.getId() + "", "【后台用户管理】用户：" + user.getuName() + " 修改了用户 " + saveBaseUser.getId() + " 的信息");

    }

    @Override
    public void delOrRecover(Integer userId, Integer currentStatus) {
        logUtil.info("delOrRecover user : {} , {}", userId,
                currentStatus);

        backendBaseAccountMapper.delOrRecover(userId, currentStatus,
                baseSqlConfig.getUserTableName());

        clearUserCache(userId);
        logUtil.info("delOrRecover user success : {} , {}", userId,
                currentStatus);

        BaseUser user = GatewayConfig.USER_LOCAL.get();
        if (currentStatus.equals(1)) {
            baseUserUtil.saveLog(user.getId() + "", "【后台用户管理】用户：" + user.getuName() + " 恢复了用户 " + userId + " 的账号状态");
        } else {
            baseUserUtil.saveLog(user.getId() + "", "【后台用户管理】用户：" + user.getuName() + " 封禁了账号 " + userId);
        }


    }

    @Override
    public TableData<?> findUserList(UserTableDto userTableReq) {

        userTableReq.format();
        TableData<UserTableVo> userTableVoTableData = new TableData<>();
        Integer totalSize = backendBaseAccountMapper.findUserListTotal(userTableReq,
                baseSqlConfig.getUserTableName(),
                baseSqlConfig.getUserJoinRoleTableName());
        userTableVoTableData.setTotalSize(totalSize);
        if (totalSize != null && totalSize > 0) {
            List<UserTableVo> userTableVos = backendBaseAccountMapper.findUserList(userTableReq,
                    baseSqlConfig.getUserTableName(),
                    baseSqlConfig.getUserJoinRoleTableName(),
                    baseSqlConfig.getRoleTableName());
            userTableVoTableData.setLists(userTableVos);
        }
        return userTableVoTableData;
    }

    @Override
    public List<MenuListVo> findUserMenuVo() {

        BaseUser currentUser = GatewayConfig.USER_LOCAL.get();
        Jedis jedis = jedisUtil.getJedis();
        try {
            // 取出该用户在缓存中的菜单列表
            // Redis命名规则，全大写，每个单词用_分割（业务 + : + 唯一标识)
            String leftMenuJson = jedis.get("EDU_SYSTEM_ADMIN_LEFT_MENU:" + currentUser.getId());
            if (org.springframework.util.StringUtils.hasLength(leftMenuJson)) {
                // 缓存中有这个用户的左菜单栏
                return JSON.parseArray(leftMenuJson, MenuListVo.class);
            }

            // 查询数据库MySQL
            List<BaseMenu> jurisdictionPos = backendBaseAccountMapper.findUserLeftMenuList(currentUser.getId(),
                    baseSqlConfig.getMenuTableName(), baseSqlConfig.getAuthorityTableName(),
                    baseSqlConfig.getUserJoinRoleTableName());

            // 创建Menu数据结构
            List<MenuListVo> menuListVos = new ArrayList<>();

            // 已经过滤了是否是菜单的问题了，现在就是说没有父子级别关系的问题。
            if (!CollectionUtils.isEmpty(jurisdictionPos)) {
                jurisdictionPos.sort(((o1, o2) -> o2.getOrdered()-o1.getOrdered()));
                // 使用Stream流
                Map<Integer, List<BaseMenu>> groupByInFather = jurisdictionPos.stream().collect(Collectors.groupingBy(BaseMenu::getParentId));
                // 取出所有的顶级父类
                List<BaseMenu> parentJurisdictionList = groupByInFather.get(0);
                if (!CollectionUtils.isEmpty(parentJurisdictionList)) {
                    parentJurisdictionList.sort(((o1, o2) -> o2.getOrdered()-o1.getOrdered()));
                    for (BaseMenu baseMenu : parentJurisdictionList) {
                        // 通过父类的id 去取出子类集合
                        menuListVos.add(new MenuListVo(baseMenu, groupByInFather.get(baseMenu.getId())));
                    }
                }

                jedis.set("EDU_SYSTEM_ADMIN_LEFT_MENU:" + currentUser.getId(), JSON.toJSONString(menuListVos));
            }
            return menuListVos;
        } finally {
            FinalUtil.unLockAndCloseRedis(jedis);
        }
    }


    /**
     * 获取用户操作日志lieb
     *
     * @param findUserLogByUserIdReq
     * @return
     * @author hxs
     */
    @Override
    public TableData<?> findUserLogByUserId(FindUserLogByUserIdDto findUserLogByUserIdReq) {
        findUserLogByUserIdReq.countStartIndex();
        findUserLogByUserIdReq.format();

        Integer total = backendBaseAccountMapper.findUserLogByUserIdTotal(findUserLogByUserIdReq.getId(), findUserLogByUserIdReq.getStartTime(), findUserLogByUserIdReq.getEndTime(), baseSqlConfig.getUserLogTableName());
        List<BaseLog> list = new ArrayList<>();
        if (total > 0) {
            list = backendBaseAccountMapper.findUserLogByUserId(findUserLogByUserIdReq.getId(), findUserLogByUserIdReq.getStartTime(), findUserLogByUserIdReq.getEndTime(), findUserLogByUserIdReq.getStartIndex(), findUserLogByUserIdReq.getLimit(), baseSqlConfig.getUserLogTableName());
        }
        TableData<BaseLog> response = new TableData<>();
        response.setLists(list);
        response.setTotalSize(total);
        return response;
    }
}
